package com.sunriz.motordrive.server.common.impl;

import com.sunriz.motordrive.domain.DAO.PvHcInfoDAO;
import com.sunriz.motordrive.domain.POJO.common.MsgRx16;
import com.sunriz.motordrive.domain.POJO.common.MsgRxTx06;
import com.sunriz.motordrive.domain.POJO.common.MsgTx0304;
import com.sunriz.motordrive.domain.DTO.Ports;
import com.sunriz.motordrive.domain.DTO.Result;
import com.sunriz.motordrive.exception.CheckException;
import com.sunriz.motordrive.exception.CrcValidationException;
import com.sunriz.motordrive.server.common.BaseSerial;
import com.sunriz.motordrive.utils.AnalyseMsg;
import com.sunriz.motordrive.utils.Crc;
import jssc.SerialPortException;
import jssc.SerialPortList;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;


import java.util.*;

/**
 * ClassName: SerialPortImpl
 * Package: com.sunriz.motordrive.server.impl
 * Description:
 *
 * @Author BuTang
 * @Create 2024/4/29 15:11
 * @Version 1.0
 */
@Slf4j
@Service
public class BaseSerialImpl implements BaseSerial {
    public static jssc.SerialPort serialPort;
    private static StringBuilder receivedData = new StringBuilder();
    private static int RxNum = 0;
    private static int TxNum = 0;
    private static int ErrNum = 0;
    long lastLogTime = System.currentTimeMillis();

    public static Map<String, String> check06Map = new HashMap<>();

    //    public static Map<Integer,Integer> mapOnlyRead;
    public static Map<Integer, Integer> mapReadAndWrite = new HashMap<>();

    public static Map<Integer, PvHcInfoDAO> result;


    public static Map returnRead = new HashMap<>();
    public static Map returnReadAndWrite = new HashMap<>();

    // 设置最大重试次数为3
    public static int maxRetries = 1;

    // 是否打印报文
    public static boolean MsgFlag = false ;


    /**
     * 所有的返回结果
     * --- 添加电机驱动配置时添加
     */
    public static Map resultAllData = new HashMap<>();


    public static boolean writeFlag = false;
    public static int slaveId;
    public static String RETURN_VALUE = String.valueOf(0);


    /**
     * 两个报文间的时间间隔
     */
    private static int SPACE_BETWEEN_FRAMES = 50;
//    private static int SPACE_BETWEEN_FRAMES = 200;
    /**
     * 是否有返回的报文
     */
    private static boolean returnMsgFlag = false;

    /**
     * 存储发送报文时的起始地址
     */
    public static int MsgStartAddr = 0;
    public static List list32;
//    public static ArrayList eachAddrByte32;

    byte[] alreadySendMsg;
    private static long lastMsgTIme = System.currentTimeMillis();

    public static int messageSendingFailure = 0;
    public static int closeTheSerialPortNum = 10;


    /**
     * 打开串口功能
     * @param port 端口号（从前端获取）
     * @return Result成功or失败
     */
    @Override
    public Result enableSerialPort(String port, int baudRate) {
        if (!port.startsWith("COM")) {
            return Result.fail("请选择需要打开的串口", "403");
        }
        disableSerialPort();
        // 判断端口是否开启，若开启，则开始定时查询数据
//            if (serialPort != null ){
//                boolean opened = serialPort.isOpened();
//                if (opened ){
//                    log.info("{}端口已开启，请勿重复开启",port);
//                    return ResultVO.fail("串口已打开，无需再次开启","403");
//                }
        serialPortStart(port, baudRate);
//                if (serialPort.isOpened()){
//                    returnMsgFlag = true;
//                    return ResultVO.success("串口开启成功","200");
//                }else{
//                    return ResultVO.fail("请进入设备管理器查看正确的端口号","403");
//                }
//            }else {
//                serialPortStart(port) ;
//                if (serialPort.isOpened()){
//                    return ResultVO.success("串口开启成功","200");
//                }
//                return ResultVO.fail("请进入设备管理器查看正确的端口号","403");
//            }
        return Result.success("串口已开启", "200");
    }


    /**
     * 关闭串口
     *
     * @return Result成功or失败
     */
    @Override
    public Result disableSerialPort() {
        jssc.SerialPort myPort = BaseSerialImpl.serialPort;
        if (myPort != null) {
            if (myPort.isOpened()) {
                try {
                    myPort.closePort();
                } catch (SerialPortException e) {
                    e.printStackTrace();
                }

                return Result.success("串口已关闭", null);
            }
            return Result.success("串口已关闭", null);
        }


        return Result.success("串口关闭成功", null);
//        if (myPort != null ){
//            boolean opened = myPort.isOpened();
//            if (opened == true){
//                try {
//                    myPort.closePort();
//                    log.info("{}端口已成功关闭",myPort.getPortName() );
//                    return ResultVO.success("串口关闭成功",null);
//                } catch (SerialPortException e) {
//                    return ResultVO.fail("关闭串口失败","400");
//                   }
//            }
//            return ResultVO.fail("串口已关闭，无需再次关闭",null);
//        }else {
//            return ResultVO.fail("串口尚未开启，无法关闭","500");
//        }
    }


    @Override
    public Result getPorts() {
        String[] portNames = SerialPortList.getPortNames();
        ArrayList list = new ArrayList();
        for (String portName : portNames) {
            list.add(new Ports(portName, portName));
        }
        return Result.success("串口id获取成功", list);
    }


    private void serialPortStart(String port, int baudRate) {
        serialPort = new jssc.SerialPort(port);
        log.info("已打开{}串口，开始接收报文", port);
        try {
            serialPort.openPort(); // 打开串口
            serialPort.setParams(
//                    921600
//                    115200
                    baudRate,
                    jssc.SerialPort.DATABITS_8,
                    jssc.SerialPort.STOPBITS_1,
                    jssc.SerialPort.PARITY_NONE);
            // 设置串口参数
            serialPort.setEventsMask(jssc.SerialPort.MASK_RXCHAR);
        } catch (SerialPortException e) {
            System.out.println("Error: " + e);
        }

        // 设置事件掩码
        try {
            serialPort.addEventListener((serialPortEvent) -> {
                // 如果收到数据
                if (serialPortEvent.isRXCHAR()) {
                    // 读取返回的数据
                    byte[] dataRead = new byte[0];
                    try {
                        dataRead = serialPort.readBytes();
                    } catch (SerialPortException e) {
                        throw new RuntimeException(e);
                    }
                    if (dataRead != null) {
                        for (int i = 0; i < dataRead.length; i++) {
                            // 将接收到的数据添加到缓存中
                            receivedData.append(String.format("%02X ", dataRead[i]));
                        }
                        // 处理消息
                        RxNum++;
                        processData(receivedData.toString());
                        receivedData.setLength(0);
                    }
                }
            });
        } catch (SerialPortException | CrcValidationException e) {
            // 将捕获的异常传递到共享变量
            System.out.println("捕获到CrcValidationException");
            CheckException.eventListenerException = e;
        }
    }


    @Override
    public Result sendModbus(byte[] bytes) {
        TxNum++;
        // 设置下发报文起始地址
        int aByte = ((int) bytes[2] & 0xFF) * 256;
        int bByte = (int) bytes[3] & 0xFF;




        // 计算crc校验码，完善报文
        String s = Crc.Crc16(bytes);
        String substring = s.substring(0, 2);
        String substring1 = s.substring(2);
        byte[] newBytes = new byte[(bytes.length + 2)];
        System.arraycopy(bytes, 0, newBytes, 0, bytes.length);
        newBytes[newBytes.length - 2] = (byte) Integer.parseInt(substring, 16);
        newBytes[newBytes.length - 1] = (byte) Integer.parseInt(substring1, 16);

        if (MsgFlag == true ){
            for (byte b : newBytes) {
                System.out.print(String.format("%02X ", b));
            }
            System.out.println();  // 每个byte[]之间换行
        }

        // 判断端口是否开启，若开启，则判断是否是下发数据，下发数据优先执行。报文发送三遍若未响应，则发送下一条报文
        if (serialPort != null) {
            boolean opened = serialPort.isOpened();
            boolean flag = false;
            if (opened == true) {
                // 已有返回报文
                if (returnMsgFlag) {
                    // 优先写入 todo 发送无相应重试
                    try {
                        Thread.sleep(SPACE_BETWEEN_FRAMES);
//                            System.out.print("节点1，有回复报文，发送消息：");
//                            System.out.print("节点1，发送消息：");
                        extracted("节点1，发送消息：", newBytes);
                        MsgStartAddr = bByte + aByte;
                        lastMsgTIme = System.currentTimeMillis();
                        if (serialPort.isOpened()) {
                            flag = serialPort.writeBytes(newBytes);
//                            log.info("Tx " + newBytes.toString());
                        }
                        alreadySendMsg = newBytes;
                        returnMsgFlag = false;
                    } catch (SerialPortException e) {
                        return Result.fail("SerialPortException", "500");
                    } catch (InterruptedException e) {
                        return Result.fail("InterruptedException", "500");
                    } finally {
                        writeFlag = false;
                        if (flag == true) {

                            return Result.success("报文下发成功，还未check", "200");
                        } else {
                            log.info("报文发送失败请重试");
                            messageSendingFailure++;
                            if (messageSendingFailure >= closeTheSerialPortNum) {
                                disableSerialPort();
                            }
                            return Result.fail("报文发送时出现了错误，请查看节点1", "500");
                        }
                    }
                }
                // 还未有返回报文
                else {

                    // 重试次数计数器
                    int retryCount = 0;
                    while (retryCount < maxRetries) {
                        long startTime = System.currentTimeMillis();
                        while (System.currentTimeMillis() - startTime < 100) {
                            if (returnMsgFlag) {
                                try {
                                    Thread.sleep(SPACE_BETWEEN_FRAMES);
//                                    System.out.print("节点2，发送消息：");
                                    extracted("节点2，发送消息：", newBytes);
                                    MsgStartAddr = bByte + aByte;
                                    lastMsgTIme = System.currentTimeMillis();
                                    if (serialPort.isOpened()) {
                                        flag = serialPort.writeBytes(newBytes);
                                    }
//                                    log.info("Tx " + newBytes.toString());
                                    alreadySendMsg = newBytes;
                                    returnMsgFlag = false;
                                } catch (SerialPortException e) {
                                    throw new RuntimeException(e);
                                } catch (InterruptedException e) {
                                    throw new RuntimeException(e);
                                } finally {
                                    writeFlag = false;
                                    if (flag == true) {
                                        return Result.success("报文下发成功，还未check", "200");
                                    } else {
//                                        log.info("无返回报文，新报文发送失败");
                                    }
                                }
                            } else {
                                // 100ms内无返回报文,重新发送报文
                                if (System.currentTimeMillis() - lastMsgTIme > 100) {
                                    try {
                                        try {
                                            Thread.sleep(SPACE_BETWEEN_FRAMES);
                                        } catch (InterruptedException e) {
                                            throw new RuntimeException(e);
                                        }
                                        extracted("节点3，发送消息：", newBytes);
                                        MsgStartAddr = bByte + aByte;
                                        lastMsgTIme = System.currentTimeMillis();
                                        if (serialPort.isOpened()) {

                                            flag = serialPort.writeBytes(newBytes);
//                                            log.info("Tx " + newBytes);
                                        }
                                        alreadySendMsg = newBytes;
                                        returnMsgFlag = false;
                                    } catch (SerialPortException e) {
                                        throw new RuntimeException(e);
                                    } finally {
                                        writeFlag = false;
                                        if (flag == true) {
                                            return Result.success("报文下发成功，还未check", "200");
                                        } else {
                                            log.info("报文发送失败请重试");
                                            messageSendingFailure++;
                                            if (messageSendingFailure >= closeTheSerialPortNum) {
                                                disableSerialPort();
                                            }
                                        }
                                    }
                                }
                            }

                            try {
                                // 睡眠，防止栈堆溢出
                                Thread.sleep(5);
                            } catch (InterruptedException e) {
                                throw new RuntimeException(e);
                            }
                        }
                        retryCount++;
                    }
                    returnMsgFlag = true;
                    System.out.println(System.currentTimeMillis() + " 报文超时");
                }
            }
        }
        return Result.fail("报文发送失败", "403");
    }


    private static void extracted(String string, byte[] newBytes) {
        byte byte06 = 6;
        if (newBytes[1] == byte06) {

            StringBuilder hexDump = new StringBuilder();
            // 遍历数组中的每个字节
            for (int i = 0; i < newBytes.length; i++) {
                // 使用String.format()来格式化每个字节为两位十六进制数
                // %02X确保即使对于较小的数字也会打印前导零
                String hex = String.format("%02X ", newBytes[i]);
                // 将格式化后的字符串追加到StringBuilder对象
                hexDump.append(hex);
                // 每打印8个字节后换行，可以根据需要调整
            }

            log.info("Tx：{}",hexDump.toString());
        }
    }

    /**
     * 开始数据的解析
     *
     * @param data 报文
     */
    private void processData(String data) {
        String[] dataSplit = data.split(" ");

        boolean crc = crcFlag(dataSplit);
        if (!crc) {
            //crc校验不通过
            ErrNum++;
            String msg = "总接收数量" + RxNum + ",总发送数量" + TxNum + ",错误数量" + ErrNum + ",报文crc校验没通过，报错报文:" + data;
//            log.error("总接收数量{}，总发送数量：{}，错误数量：{}， 报文解析失败，报文crc校验没通过，不处理报文:{}",RxNum,TxNum,ErrNum,data);
            log.info(msg);
            CheckException.eventListenerException = new CrcValidationException(msg);

        } else {
            // crc检验通过，进行处理报文
            returnMsgFlag = true;
            processingMessage(data, dataSplit);
        }
        // 获取当前时间
        long currentTime = System.currentTimeMillis();
        // 检查是否已经过了十秒
        if (currentTime - lastLogTime >= 10000) {
            log.info("总接收数量{}，总发送数量：{}，错误数量：{}", RxNum, TxNum, ErrNum);
            lastLogTime = currentTime;
        }
    }


    private void processingMessage(String data, String[] dataSplit) {


        returnMsgFlag = true;

        //获取功能码 06
        Integer model = Integer.valueOf(dataSplit[1]);
        switch (model) {

            case 06: {

                log.info("Rx：{}",data);

                MsgRxTx06 msgRxTx06 = AnalyseMsg.Rx06(data);
                Integer addr = msgRxTx06.addr;
                Integer value = msgRxTx06.data;
                check06Map.put(addr.toString(), value.toString());

                RETURN_VALUE = String.valueOf(value);
                //读取数据，并且将addr&value存储到map中
                if (list32.contains(addr)) {
                    // 三十二进制数
                    try {


                        if (mapReadAndWrite.containsKey(addr)) {
                            returnReadAndWrite.put(addr, value);
                        } else {
                            returnRead.put(addr, value);
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                } else {
                    // 以下是十六进制的数
                    if (mapReadAndWrite.containsKey(addr)) {
                        returnReadAndWrite.put(addr, value);
                    } else {
                        returnRead.put(addr, value);
                    }
                }

//                todo 2.0版本
//                PvHcInfoDAO info = result.get(addr);
//                info.setValue(value);
//                result.put(addr,info);

                /**
                 * 所有的返回结果
                 * --- 添加电机驱动配置时添加
                 */
                resultAllData.put(addr, value);

                break;
            }
            case 10: {
                log.info("Rx：{}",data);
                MsgRx16 msgRx16 = AnalyseMsg.Rx16(data);
                Integer startAddr = msgRx16.getStartAddr();
                check06Map.put(startAddr.toString(), null);
                break;
            }
            case 03:
            case 04: {
                MsgTx0304 msgTx0304 = AnalyseMsg.Tx0304(data);

                List<Integer> list = msgTx0304.data;
                int msgStartAddr = MsgStartAddr;
                String value = String.valueOf(0);


                //读取数据，并且将addr&value存储到map中
                if (list32.contains(msgStartAddr)) {
                    // 三十二进制数
                    try {
                        for (int i = 0; i < list.size(); i = i + 4) {
//                            value = String.valueOf(list.get(i) * 256 * 256 * 256 + list.get(i + 1) * 256 * 256 + list.get(i + 2) * 256 + list.get(i + 3));

                            // 假设 list 中的元素是字节（0~255 的整数），将其拼接成一个 32 位的无符号整数
                            value = String.valueOf((list.get(i) & 0xFFL) << 24 |
                                    (list.get(i + 1) & 0xFFL) << 16 |
                                    (list.get(i + 2) & 0xFFL) << 8 |
                                    (list.get(i + 3) & 0xFFL));

                            try {
                                if (mapReadAndWrite.containsKey(MsgStartAddr)) {
                                    returnReadAndWrite.put(msgStartAddr, value);
                                    RETURN_VALUE = value;
                                } else {
                                    returnRead.put(msgStartAddr, value);
                                    RETURN_VALUE = value;
                                }
                            } catch (Exception e) {
                                e.printStackTrace();
                            }

//                            // todo 2.0
//                            PvHcInfoDAO info = result.get(msgStartAddr);
//                            info.setValue(value);
//                            result.put(msgStartAddr,info);
//

                            /**
                             * 所有的返回结果
                             * --- 添加电机驱动配置时添加
                             */
                            resultAllData.put(msgStartAddr, value);
                            msgStartAddr += 2;
                        }
                    } catch (Exception e) {
                        log.info("报错报文为：{}", data);
                        e.printStackTrace();
                    }
                } else {
                    // 以下是十六进制的数
                    for (int i = 0; i < list.size(); i = i + 2) {
                        value = String.valueOf(list.get(i) * 256 + list.get(i + 1));

                        //
                        RETURN_VALUE = value;

                        try {
                            if (mapReadAndWrite != null && mapReadAndWrite.containsKey(MsgStartAddr)) {
                                returnReadAndWrite.put(msgStartAddr, value);
                            } else {
                                returnRead.put(msgStartAddr, value);
                            }
                        } catch (NullPointerException e) {
                            CheckException.eventListenerException = new NullPointerException("空指针异常，mapReadAndWrite为空");
                        }

                        // todo 2.0
//                        PvHcInfoDAO info = result.get(msgStartAddr);
//                        info.setValue(value);
//                        result.put(msgStartAddr,info);

                        /**
                         * 所有的返回结果
                         * --- 添加电机驱动配置时添加
                         */
                        resultAllData.put(msgStartAddr, value);
                        msgStartAddr += 1;
                    }
                }
                break;
            }
//            case 10:{
//                break;
//            }
            case 83:
            case 84:
            case 86:
            case 90: {
                ErrNum++;
//                System.out.print("发生错误的发送报文为:");
                log.error("异常报文：{}",data);
                for (int i = 0; i < alreadySendMsg.length; i++) {
                    if (alreadySendMsg[i] < 0) {
                        int maxUnsignedValue = (1 << 8) - 1;
                        // 将有符号整数转换为无符号整数
                        int unsignedInt = alreadySendMsg[i] & maxUnsignedValue;
                        // 将无符号整数转换为十六进制字符串
//                        System.out.print(String.format("%02X", unsignedInt) + " ");
                    } else {
                        // 直接将整数转换为十六进制字符串
//                        System.out.print(String.format("%02X", alreadySendMsg[i]) + " ");
                    }
                }


                log.error("总接收数量{}，总发送数量：{}，错误数量：{}， 报文解析失败，错误报文为：{}", RxNum, TxNum, ErrNum, data);
                break;
            }
            default:
                log.error("出现了意料外的报文{},请注意查看", data);
        }
    }

    /**
     * crc校验判断
     *
     * @param dataSplit 报文
     * @return 是否通过crc校验
     */
    private static boolean crcFlag(String[] dataSplit) {

        if (dataSplit.length >= 2) {
            // 创建一个与 byteStrings 长度相同的 byte 数组
            byte[] crcByteArray = new byte[dataSplit.length - 2];

            // 将每个字符串解析为十六进制并放入 byte 数组
            for (int i = 0; i < crcByteArray.length; i++) {
                crcByteArray[i] = (byte) Integer.parseInt(dataSplit[i], 16);
            }

            //进行crc校验
            String s = Crc.Crc16(crcByteArray);
            String firstString = s.substring(0, 2);
            String secondString = s.substring(2, 4);
            boolean flag = firstString.equals(dataSplit[dataSplit.length - 2]) && secondString.equals(dataSplit[dataSplit.length - 1]);
            return flag;
        } else {
            return false;
        }
    }
}
